package com.gitee.wsl.compose.ui.richtext.edit.state

import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.text.*
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import com.gitee.wsl.compose.ext.text.DefaultParagraphStyle
import com.gitee.wsl.compose.ui.richtext.edit.annotation.ExperimentalRichTextApi
import com.gitee.wsl.compose.ext.text.customMerge
import com.gitee.wsl.compose.ui.richtext.edit.model.span.RichSpan
import com.gitee.wsl.compose.ui.richtext.edit.model.span.style.RichSpanStyle
import com.gitee.wsl.compose.ui.richtext.edit.model.RichTextConfig
import com.gitee.wsl.compose.ext.text.unmerge
import com.gitee.wsl.compose.ui.richtext.edit.model.paragraph.RichParagraph
import com.gitee.wsl.compose.ui.richtext.edit.model.paragraph.type.DefaultParagraphType
import com.gitee.wsl.compose.ui.richtext.edit.model.paragraph.type.list.OrderedList
import com.gitee.wsl.compose.ui.richtext.edit.model.paragraph.type.ParagraphType
import com.gitee.wsl.compose.ui.richtext.edit.model.paragraph.type.list.UnorderedList
import com.gitee.wsl.compose.ui.richtext.edit.parser.html.RichTextStateHtmlParser
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.reflect.KClass
import kotlin.text.substring


@Composable
fun rememberRichTextState(): RichTextState {
    return rememberSaveable(saver = RichTextState.Saver) {
        RichTextState()
    }
}

@OptIn(ExperimentalRichTextApi::class)
class RichTextState internal constructor(
    initialRichParagraphList: List<RichParagraph>,
) {
    constructor() : this(listOf(RichParagraph()))

    internal val richParagraphList = mutableStateListOf<RichParagraph>()
    internal var visualTransformation: VisualTransformation by mutableStateOf(VisualTransformation.None)
    internal var textFieldValue by mutableStateOf(defaultTextFieldValue)
        internal set

    /**
     * The annotated string representing the rich text.
     */
    var annotatedString by mutableStateOf(AnnotatedString(text = ""))
        internal set

    val composition get() = textFieldValue.composition

    internal var singleParagraphMode by mutableStateOf(false)

    internal var textLayoutResult: TextLayoutResult? by mutableStateOf(null)
        internal set

    internal var lastPressPosition: Offset? by mutableStateOf(null)

    internal var currentAppliedSpanStyle: SpanStyle by mutableStateOf(
        getRichSpanByTextIndex(textIndex = selection.min - 1)?.fullSpanStyle
            ?: RichSpanStyle.DefaultSpanStyle
    )

    /**
     * The current rich span style.
     * If the selection is collapsed, the rich span style is the style of the character preceding the selection.
     * If the selection is not collapsed, the rich span style is the style of the selection.
     */
    val currentRichSpanStyle: RichSpanStyle
        get() = when {
            currentAppliedRichSpanStyle::class == RichSpanStyle.Default::class ->
                toAddRichSpanStyle

            currentAppliedRichSpanStyle::class == toRemoveRichSpanStyleKClass ->
                RichSpanStyle.Default

            else ->
                currentAppliedRichSpanStyle
        }


    var currentAppliedRichSpanStyle: RichSpanStyle by mutableStateOf(
        getRichSpanByTextIndex(textIndex = selection.min - 1)?.richSpanStyle
            ?: RichSpanStyle.Default
    )

    internal var toAddSpanStyle: SpanStyle by mutableStateOf(RichSpanStyle.DefaultSpanStyle)
    internal var toRemoveSpanStyle: SpanStyle by mutableStateOf(RichSpanStyle.DefaultSpanStyle)

    internal var toAddRichSpanStyle: RichSpanStyle = RichSpanStyle.Default
    internal var toRemoveRichSpanStyle: KClass<out RichSpanStyle> = RichSpanStyle.Default::class

    internal var toRemoveRichSpanStyleKClass: KClass<out RichSpanStyle> by mutableStateOf(
        RichSpanStyle.Default::class
    )

    /**
     * The current span style.
     * If the selection is collapsed, the span style is the style of the character preceding the selection.
     * If the selection is not collapsed, the span style is the style of the selection.
     */
    val currentSpanStyle: SpanStyle
        get() = currentAppliedSpanStyle.customMerge(toAddSpanStyle).unmerge(toRemoveSpanStyle)

    internal var styledRichSpanList = mutableStateListOf<RichSpan>()


    internal var currentAppliedParagraphStyle: ParagraphStyle by mutableStateOf(
        getRichParagraphByTextIndex(textIndex = selection.min - 1)?.paragraphStyle
            ?: richParagraphList.firstOrNull()?.paragraphStyle
            ?: DefaultParagraphStyle
    )

    internal var toAddParagraphStyle: ParagraphStyle by mutableStateOf(DefaultParagraphStyle)
    internal var toRemoveParagraphStyle: ParagraphStyle by mutableStateOf(DefaultParagraphStyle)

    /**
     * The current paragraph style.
     * If the selection is collapsed, the paragraph style is the style of the paragraph containing the selection.
     * If the selection is not collapsed, the paragraph style is the style of the selection.
     */
    val currentParagraphStyle: ParagraphStyle
        get() = currentAppliedParagraphStyle.merge(toAddParagraphStyle).unmerge(toRemoveParagraphStyle)

    internal var currentRichParagraphType: ParagraphType by mutableStateOf(
        getRichParagraphByTextIndex(textIndex = selection.min - 1)?.type
            ?: DefaultParagraphType()
    )

    var isUnorderedList: Boolean by mutableStateOf(currentRichParagraphType is UnorderedList)

    var isOrderedList: Boolean by mutableStateOf(currentRichParagraphType is OrderedList)

    var canIncreaseListLevel: Boolean by mutableStateOf(false)
    var canDecreaseListLevel: Boolean by mutableStateOf(false)

    var isList: Boolean by mutableStateOf(isUnorderedList || isOrderedList)

    val config: RichTextConfig = RichTextConfig(
        updateText = {
            updateTextFieldValue(textFieldValue)
        }
    )

    internal val inlineContentMap = mutableStateMapOf<String, InlineTextContent>()
    internal val usedInlineContentMapKeys = mutableSetOf<String>()

    init {
        updateRichParagraphList(initialRichParagraphList)
    }

    internal fun initState(){
        toAddSpanStyle = RichSpanStyle.DefaultSpanStyle
        toRemoveSpanStyle = RichSpanStyle.DefaultSpanStyle
        toAddRichSpanStyle = RichSpanStyle.Default
        toRemoveRichSpanStyleKClass = RichSpanStyle.Default::class
    }

    internal fun addText(
        text: String,
        index: Int,
    ) {
        val beforeText = textFieldValue.text.substring(0, index)
        val afterText = textFieldValue.text.substring(selection.max)
        val newText = "$beforeText$text$afterText"

        onTextFieldValueChange(
            newTextFieldValue = textFieldValue.copy(
                text = newText,
                selection = TextRange(index + text.length),
            )
        )
    }

    /**
     * Updates the [RichTextState] with the given [text].
     *
     * @param text The text to update the [RichTextState] with.
     */
    fun setText(text: String): RichTextState {
        val textFieldValue =
            TextFieldValue(
                text = text,
                selection = TextRange(text.length),
            )

        onTextFieldValueChange(
            newTextFieldValue = textFieldValue
        )
        return this
    }

    /**
     * Temporarily stores the new text field value, before it is validated.
     */
    internal var tempTextFieldValue = textFieldValue


    /**x
     * Removes the specified text range from the current text.
     *
     * @param textRange the range of text to be removed
     */
    internal fun removeTextRange(
        textRange: TextRange
    ) {
        onTextFieldValueChange(
            newTextFieldValue = textFieldValue.copy(
                text = textFieldValue.text.removeRange(
                    startIndex = selection.min,
                    endIndex = selection.max,
                ),
                selection = TextRange(textRange.min),
            )
        )
    }

    private var registerLastPressPositionJob: Job? = null

    internal suspend fun registerLastPressPosition(pressPosition: Offset): Unit = coroutineScope {
        registerLastPressPositionJob?.cancel()
        registerLastPressPositionJob = launch {
            lastPressPosition = pressPosition
            delay(300)
            lastPressPosition = null
        }
    }


    /**
     * Updates the [RichTextState] with the given [markdown].
     *
     * @param markdown The markdown to update the [RichTextState] with.
     */
    /*fun setMarkdown(markdown: String): RichTextState {
        val richParagraphList = RichTextStateMarkdownParser.encode(markdown).richParagraphList
        updateRichParagraphList(richParagraphList)
        return this
    }*/

    /**
     * Decodes the [RichTextState] to a markdown string.
     *
     * @return The html string.
     */
    /*fun toMarkdown(): String {
        return RichTextStateMarkdownParser.decode(this)
    }*/

    /**
     * Clears the [RichTextState] and sets the [TextFieldValue] to an empty value.
     */
    fun clear() {
        richParagraphList.clear()
        richParagraphList.add(RichParagraph())
        updateTextFieldValue(defaultTextFieldValue)
    }

    companion object {
        val defaultTextFieldValue = TextFieldValue()

        val Saver: Saver<RichTextState, *> = listSaver(
            save = {
                listOf(
                    it.toHtml(),
                    it.selection.start.toString(),
                    it.selection.end.toString(),
                )
            },
            restore = {
                val html = it[0]
                val selectionStart = it[1].toInt()
                val selectionEnd = it[2].toInt()
                val selection = TextRange(selectionStart, selectionEnd)
                val richTextState = RichTextState()
                richTextState.setHtml(html)
                richTextState.updateTextFieldValue(
                    richTextState.textFieldValue.copy(
                        selection = selection
                    )
                )
                richTextState
            }
        )

    }
}

